home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / swtools / mipsABI / examples / sup / PORT / step03 / supfilesrv.c < prev   
Encoding:
Text File  |  1994-08-02  |  51.7 KB  |  1,826 lines

  1. /*
  2.  * Copyright (c) 1992 Carnegie Mellon University
  3.  * All Rights Reserved.
  4.  * 
  5.  * Permission to use, copy, modify and distribute this software and its
  6.  * documentation is hereby granted, provided that both the copyright
  7.  * notice and this permission notice appear in all copies of the
  8.  * software, derivative works or modified versions, and any portions
  9.  * thereof, and that both notices appear in supporting documentation.
  10.  *
  11.  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  12.  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  13.  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  14.  *
  15.  * Carnegie Mellon requests users of this software to return to
  16.  *
  17.  *  Software Distribution Coordinator  or  Software_Distribution@CS.CMU.EDU
  18.  *  School of Computer Science
  19.  *  Carnegie Mellon University
  20.  *  Pittsburgh PA 15213-3890
  21.  *
  22.  * any improvements or extensions that they make and grant Carnegie Mellon
  23.  * the rights to redistribute these changes.
  24.  *
  25. /*
  26.  * supfilesrv -- SUP File Server
  27.  *
  28.  * Usage:  supfilesrv [-l] [-P] [-N] [-R]
  29.  *    -l    "live" -- don't fork daemon
  30.  *    -P    "debug ports" -- use debugging network ports
  31.  *    -N    "debug network" -- print debugging messages for network i/o
  32.  *    -R    "RCS mode" -- if file is an rcs file, use co to get contents
  33.  *
  34.  **********************************************************************
  35.  * HISTORY
  36.  * 13-Sep-92  Mary Thompson (mrt) at Carnegie-Mellon University
  37.  *    Changed name of sup program in xpatch from /usr/cs/bin/sup to
  38.  *    /usr/bin/sup for exported version of sup.
  39.  *
  40.  * 7-July-93  Nate Williams at Montana State University
  41.  *    Modified SUP to use gzip based compression when sending files
  42.  *    across the network to save BandWidth
  43.  *
  44.  * $Log: supfilesrv.c,v $
  45.  * Revision 1.5  1993/08/04  17:46:21  brezak
  46.  * Changes from nate for gzip'ed sup
  47.  *
  48.  * Revision 1.3  1993/06/05  21:32:17  cgd
  49.  * use daemon() to put supfilesrv into daemon mode...
  50.  *
  51.  * Revision 1.2  1993/05/24  17:57:31  brezak
  52.  * Remove netcrypt.c. Remove unneeded files. Cleanup make.
  53.  *
  54.  * Revision 1.20  92/09/09  22:05:00  mrt
  55.  *     Added Brad's change to make sendfile take a va_list.
  56.  *     Added support in login to accept an non-encrypted login
  57.  *     message if no user or password is being sent. This supports
  58.  *     a non-crypting version of sup. Also fixed to skip leading
  59.  *     white space from crypts in host files.
  60.  *     [92/09/01            mrt]
  61.  * 
  62.  * Revision 1.19  92/08/11  12:07:59  mrt
  63.  *         Made maxchildren a patchable variable, which can be set by the
  64.  *         command line switch -C or else defaults to the MAXCHILDREN
  65.  *         defined in sup.h. Added most of Brad's STUMP changes.
  66.  *     Increased PGMVERSION to 12 to reflect substantial changes.
  67.  *     [92/07/28            mrt]
  68.  * 
  69.  * Revision 1.18  90/12/25  15:15:39  ern
  70.  *     Yet another rewrite of the logging code. Make up the text we will write
  71.  *        and then get in, write it and get out.
  72.  *     Also set error on write-to-full-disk if the logging is for recording
  73.  *        server is busy.
  74.  *     [90/12/25  15:15:15  ern]
  75.  * 
  76.  * Revision 1.17  90/05/07  09:31:13  dlc
  77.  *     Sigh, some more fixes to the new "crypt" file handling code.  First,
  78.  *     just because the "crypt" file is in a local file system does not mean
  79.  *     it can be trusted.  We have to check for hard links to root owned
  80.  *     files whose contents could be interpretted as a crypt key.  For
  81.  *     checking this fact, the new routine stat_info_ok() was added.  This
  82.  *     routine also makes other sanity checks, such as owner only permission,
  83.  *     the file is a regular file, etc.  Also, even if the uid/gid of th
  84.  *     "crypt" file is not going to be used, still use its contents in order
  85.  *     to cause fewer surprises to people supping out of a shared file system
  86.  *     such as AFS.
  87.  *     [90/05/07            dlc]
  88.  * 
  89.  * Revision 1.16  90/04/29  04:21:08  dlc
  90.  *     Fixed logic bug in docrypt() which would not get the stat information
  91.  *     from the crypt file if the crypt key had already been set from a
  92.  *     "host" file.
  93.  *     [90/04/29            dlc]
  94.  * 
  95.  * Revision 1.15  90/04/18  19:51:27  dlc
  96.  *     Added the new routines local_file(), link_nofollow() for use in
  97.  *     dectecting whether a file is located in a local file system.  These
  98.  *     routines probably should have been in another module, but only
  99.  *     supfilesrv needs to do the check and none of its other modules seemed
  100.  *     appropriate.  Note, the implementation should be changed once we have
  101.  *     direct kernel support, for example the fstatfs(2) system call, for
  102.  *     detecting the type of file system a file resides.  Also, I changed
  103.  *     the routines which read the crosspatch crypt file or collection crypt
  104.  *     file to save the uid and gid from the stat information obtained via
  105.  *     the local_file() call (when the file is local) at the same time the
  106.  *     crypt key is read.  This change disallows non-local files for the
  107.  *     crypt key to plug a security hole involving the usage of the uid/gid
  108.  *     of the crypt file to define who the the file server should run as.  If
  109.  *     the saved uid/gid are both valid, then the server will set its uid/gid
  110.  *     to these values.
  111.  *     [90/04/18            dlc]
  112.  * 
  113.  * Revision 1.14  89/08/23  14:56:15  gm0w
  114.  *     Changed msgf routines to msg routines.
  115.  *     [89/08/23            gm0w]
  116.  * 
  117.  * Revision 1.13  89/08/03  19:57:33  mja
  118.  *     Remove setaid() call.
  119.  * 
  120.  * Revision 1.12  89/08/03  19:49:24  mja
  121.  *     Updated to use v*printf() in place of _doprnt().
  122.  *     [89/04/19            mja]
  123.  * 
  124.  * 11-Sep-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
  125.  *    Added code to record release name in logfile.
  126.  *
  127.  * 18-Mar-88  Glenn Marcy (gm0w) at Carnegie-Mellon University
  128.  *    Added host=<hostfile> support to releases file. [V7.12]
  129.  *
  130.  * 27-Dec-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
  131.  *    Added crosspatch support.  Created docrypt() routine for crypt
  132.  *    test message.
  133.  *
  134.  * 09-Sep-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
  135.  *    Removed common information logging code, the quiet switch, and
  136.  *    moved samehost() check to after device/inode check.
  137.  *
  138.  * 28-Jun-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
  139.  *    Added code for "release" support. [V5.11]
  140.  *
  141.  * 26-May-87  Doug Philips (dwp) at Carnegie-Mellon University
  142.  *    Added code to record final status of client in logfile. [V5.10]
  143.  *
  144.  * 22-May-87  Chriss Stephens (chriss) at Carnegie Mellon University
  145.  *    Mergered divergent CS and ECE versions. [V5.9a]
  146.  *
  147.  * 20-May-87  Glenn Marcy (gm0w) at Carnegie-Mellon University
  148.  *    Removed support for version 3 of SUP protocol.  Added changes
  149.  *    to make lint happy.  Added calls to new logging routines. [V5.9]
  150.  *
  151.  * 31-Mar-87  Dan Nydick (dan) at Carnegie-Mellon University
  152.  *    Fixed so no password check is done when crypts are used.
  153.  *
  154.  * 25-Nov-86  Rudy Nedved (ern) at Carnegie-Mellon University
  155.  *    Set F_APPEND fcntl in logging to increase the chance
  156.  *    that the log entry from this incarnation of the file
  157.  *    server will not be lost by another incarnation. [V5.8]
  158.  *
  159.  * 20-Oct-86  Dan Nydick (dan) at Carnegie-Mellon University
  160.  *    Changed not to call okmumbles when not compiled with CMUCS.
  161.  *
  162.  * 04-Aug-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
  163.  *    Added code to increment scmdebug as more -N flags are
  164.  *    added. [V5.7]
  165.  *
  166.  * 25-May-86  Jonathan J. Chew (jjc) at Carnegie-Mellon University
  167.  *    Renamed local variable in main program from "sigmask" to
  168.  *    "signalmask" to avoid name conflict with 4.3BSD identifier.
  169.  *    Conditionally compile in calls to CMU routines, "setaid" and
  170.  *    "logaccess". [V5.6]
  171.  *
  172.  * 21-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
  173.  *    Changed supfilesrv to use the crypt file owner and group for
  174.  *    access purposes, rather than the directory containing the crypt
  175.  *    file. [V5.5]
  176.  *
  177.  * 07-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
  178.  *    Added code to keep logfiles in repository collection directory.
  179.  *    Added code for locking collections. [V5.4]
  180.  *
  181.  * 05-Jan-86  Glenn Marcy (gm0w) at Carnegie-Mellon University
  182.  *    Added code to support new FSETUPBUSY return.  Now accepts all
  183.  *    connections and tells any clients after the 8th that the
  184.  *    fileserver is busy.  New clients will retry again later. [V5.3]
  185.  *
  186.  * 29-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  187.  *    Major rewrite for protocol version 4. [V4.2]
  188.  *
  189.  * 12-Dec-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  190.  *    Fixed close of crypt file to use file pointer as argument
  191.  *    instead of string pointer.
  192.  *
  193.  * 24-Nov-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  194.  *    Allow "!hostname" lines and comments in collection "host" file.
  195.  *
  196.  * 13-Nov-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  197.  *    Don't use access() on symbolic links since they may not point to
  198.  *    an existing file.
  199.  *
  200.  * 22-Oct-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  201.  *    Added code to restrict file server availability to when it has
  202.  *    less than or equal to eight children.
  203.  *
  204.  * 22-Sep-85  Glenn Marcy (gm0w) at Carnegie-Mellon University
  205.  *    Merged 4.1 and 4.2 versions together.
  206.  *
  207.  * 04-Jun-85  Steven Shafer (sas) at Carnegie-Mellon University
  208.  *    Created for 4.2 BSD.
  209.  *
  210.  **********************************************************************
  211.  */
  212.  
  213. #include <libc.h>
  214. #ifdef AFS
  215. #include <afs/param.h>
  216. #undef MAXNAMLEN
  217. #endif
  218. #include <sys/param.h>
  219. #include <c.h>
  220. #include <signal.h>
  221. #include <errno.h>
  222. #include <setjmp.h>
  223. #include <pwd.h>
  224. #include <grp.h>
  225. #if __STDC__
  226. #include <stdarg.h>
  227. #else
  228. #include <varargs.h>
  229. #endif
  230. #include <sys/time.h>
  231. #include <sys/resource.h>
  232. #include <sys/wait.h>
  233. #include <sys/stat.h>
  234. #include <sys/file.h>
  235. #include <sys/dir.h>
  236. #if    MACH
  237. #include <sys/ioctl.h>
  238. #endif
  239. #if    CMUCS
  240. #include <acc.h>
  241. #include <sys/ttyloc.h>
  242. #include <access.h>
  243. #include <sys/viceioctl.h>
  244. #else    CMUCS
  245. #define ACCESS_CODE_OK        0
  246. #define ACCESS_CODE_BADPASSWORD (-2)
  247. #endif  CMUCS
  248. #include "sup.h"
  249. #define MSGFILE
  250. #include "supmsg.h"
  251.  
  252. #ifdef    lint
  253. /*VARARGS1*//*ARGSUSED*/
  254. static void quit(status) {};
  255. #endif    /* lint */
  256.  
  257. extern int errno;
  258. long time ();
  259. uid_t getuid ();
  260.  
  261. int maxchildren;
  262.  
  263. /*
  264.  * These are used to save the stat information from the crosspatch crypt
  265.  * file or collection crypt file at the time it is opened for the crypt
  266.  * key and it is verified to be a local file.
  267.  */
  268. int runas_uid = -1;
  269. int runas_gid = -1;
  270.  
  271. #define PGMVERSION 13
  272.  
  273. /*************************
  274.  ***    M A C R O S    ***
  275.  *************************/
  276.  
  277. #define HASHBITS 8
  278. #define HASHSIZE (1<<HASHBITS)
  279. #define HASHMASK (HASHSIZE-1)
  280. #define HASHFUNC(x,y) ((x)&HASHMASK)
  281.  
  282. /*******************************************
  283.  ***    D A T A   S T R U C T U R E S    ***
  284.  *******************************************/
  285.  
  286. struct hashstruct {            /* hash table for number lists */
  287.     int Hnum1;            /* numeric keys */
  288.     int Hnum2;
  289.     char *Hname;            /* string value */
  290.     TREE *Htree;            /* TREE value */
  291.     struct hashstruct *Hnext;
  292. };
  293. typedef struct hashstruct HASH;
  294.  
  295. /*********************************************
  296.  ***    G L O B A L   V A R I A B L E S    ***
  297.  *********************************************/
  298.  
  299. char program[] = "supfilesrv";        /* program name for SCM messages */
  300. int progpid = -1;            /* and process id */
  301.  
  302. jmp_buf sjbuf;                /* jump location for network errors */
  303. TREELIST *listTL;            /* list of trees to upgrade */
  304.  
  305. int live;                /* -l flag */
  306. int dbgportsq;                /* -P flag */
  307. extern int scmdebug;            /* -N flag */
  308. extern int netfile;
  309. #ifdef RCS
  310. int candorcs;                /* -R flag */
  311. int dorcs = FALSE;
  312. #endif
  313.  
  314. char *clienthost;            /* host name of client */
  315. int nchildren;                /* number of children that exist */
  316. char *prefix;                /* collection pathname prefix */
  317. char *release;                /* collection release name */
  318. char *cryptkey;                /* encryption key if non-null */
  319. #ifdef CVS
  320. char *cvs_root;                /* RCS root */
  321. #endif
  322. char *rcs_branch;            /* RCS branch name */
  323. int lockfd;                /* descriptor of lock file */
  324.  
  325. /* global variables for scan functions */
  326. int trace = FALSE;            /* directory scan trace */
  327. int cancompress = FALSE;        /* Can we compress files */
  328. int docompress = FALSE;            /* Do we compress files */
  329.  
  330. HASH *uidH[HASHSIZE];            /* for uid and gid lookup */
  331. HASH *gidH[HASHSIZE];
  332. HASH *inodeH[HASHSIZE];            /* for inode lookup for linked file check */
  333.  
  334. char *fmttime ();            /* time format routine */
  335.  
  336. /*************************************
  337.  ***    M A I N   R O U T I N E    ***
  338.  *************************************/
  339.  
  340. main (argc,argv)
  341. int argc;
  342. char **argv;
  343. {
  344.     register int x,pid,signalmask;
  345.     struct sigvec chldvec,ignvec,oldvec;
  346.     int chldsig ();
  347.     long tloc;
  348.  
  349.     /* initialize global variables */
  350.     pgmversion = PGMVERSION;    /* export version number */
  351.     server = TRUE;            /* export that we're not a server */
  352.     collname = NULL;        /* no current collection yet */
  353.     maxchildren = MAXCHILDREN;    /* defined in sup.h */
  354.  
  355.     init (argc,argv);        /* process arguments */
  356.  
  357. #ifdef HAS_DAEMON
  358.     if (!live)            /* if not debugging, turn into daemon */
  359.         daemon(0, 0);
  360. #endif
  361.  
  362.     logopen ("supfile");
  363.     tloc = time ((long *)NULL);
  364.     loginfo ("SUP File Server Version %d.%d (%s) starting at %s",
  365.         PROTOVERSION,PGMVERSION,scmversion,fmttime (tloc));
  366.     if (live) {
  367.         x = service ();
  368.         if (x != SCMOK)
  369.             logquit (1,"Can't connect to network");
  370.         answer ();
  371.         (void) serviceend ();
  372.         exit (0);
  373.     }
  374.     ignvec.sv_handler = SIG_IGN;
  375.     ignvec.sv_onstack = 0;
  376.     ignvec.sv_mask = 0;
  377.     (void) sigvec (SIGHUP,&ignvec,&oldvec);
  378.     (void) sigvec (SIGINT,&ignvec,&oldvec);
  379.     (void) sigvec (SIGPIPE,&ignvec,&oldvec);
  380.     chldvec.sv_handler = chldsig;
  381.     chldvec.sv_mask = 0;
  382.     chldvec.sv_onstack = 0;
  383.     (void) sigvec (SIGCHLD,&chldvec,&oldvec);
  384.     nchildren = 0;
  385.     for (;;) {
  386.         x = service ();
  387.         if (x != SCMOK) {
  388.             logerr ("Error in establishing network connection");
  389.             (void) servicekill ();
  390.             continue;
  391.         }
  392.         signalmask = sigblock(sigmask(SIGCHLD));
  393.         if ((pid = fork()) == 0) { /* server process */
  394.             (void) serviceprep ();
  395.             answer ();
  396.             (void) serviceend ();
  397.             exit (0);
  398.         }
  399.         (void) servicekill ();    /* parent */
  400.         if (pid > 0) nchildren++;
  401.         (void) sigsetmask(signalmask);
  402.     }
  403. }
  404.  
  405. /*
  406.  * Child status signal handler
  407.  */
  408.  
  409. chldsig()
  410. {
  411.     union wait w;
  412.  
  413.     while (wait3(&w, WNOHANG, (struct rusage *)0) > 0) {
  414.         if (nchildren) nchildren--;
  415.     }
  416. }
  417.  
  418. /*****************************************
  419.  ***    I N I T I A L I Z A T I O N    ***
  420.  *****************************************/
  421.  
  422. usage ()
  423. {
  424.     quit (1,"Usage: supfilesrv [ -l | -P | -N | -C <max children> | -H <host> <user> <cryptfile> <supargs> ]\n");
  425. }
  426.  
  427. init (argc,argv)
  428. int argc;
  429. char **argv;
  430. {
  431.     register int i;
  432.     register int x;
  433.     char *clienthost,*clientuser;
  434.     char *p,*q;
  435.     char buf[STRINGLENGTH];
  436.     int maxsleep;
  437.     register FILE *f;
  438.  
  439. #ifdef RCS
  440.         candorcs = FALSE;
  441. #endif
  442.     live = FALSE;
  443.     dbgportsq = FALSE;
  444.     scmdebug = 0;
  445.     clienthost = NULL;
  446.     clientuser = NULL;
  447.     maxsleep = 5;
  448.     if (--argc < 0)
  449.         usage ();
  450.     argv++;
  451.     while (clienthost == NULL && argc > 0 && argv[0][0] == '-') {
  452.         switch (argv[0][1]) {
  453.         case 'l':
  454.             live = TRUE;
  455.             break;
  456.         case 'P':
  457.             dbgportsq = TRUE;
  458.             break;
  459.         case 'N':
  460.             scmdebug++;
  461.             break;
  462.         case 'C':
  463.             if (--argc < 1)
  464.                 quit (1,"Missing arg to -C\n");
  465.             argv++;
  466.             maxchildren = atoi(argv[0]);
  467.             break;
  468.         case 'H':
  469.             if (--argc < 3)
  470.                 quit (1,"Missing args to -H\n");
  471.             argv++;
  472.             clienthost = argv[0];
  473.             clientuser = argv[1];
  474.             cryptkey = argv[2];
  475.             argc -= 2;
  476.             argv += 2;
  477.             break;
  478. #ifdef RCS
  479.                 case 'R':
  480.                         candorcs = TRUE;
  481.                         break;
  482. #endif
  483.         default:
  484.             fprintf (stderr,"Unknown flag %s ignored\n",argv[0]);
  485.             break;
  486.         }
  487.         --argc;
  488.         argv++;
  489.     }
  490.     if (clienthost == NULL) {
  491.         if (argc != 0)
  492.             usage ();
  493.         x = servicesetup (dbgportsq ? DEBUGFPORT : FILEPORT);
  494.         if (x != SCMOK)
  495.             quit (1,"Error in network setup");
  496.         for (i = 0; i < HASHSIZE; i++)
  497.             uidH[i] = gidH[i] = inodeH[i] = NULL;
  498.         return;
  499.     }
  500.     server = FALSE;
  501.     if (argc < 1)
  502.         usage ();
  503.     f = fopen (cryptkey,"r");
  504.     if (f == NULL)
  505.         quit (1,"Unable to open cryptfile %s\n",cryptkey);
  506.     if (p = fgets (buf,STRINGLENGTH,f)) {
  507.         if (q = index (p,'\n'))  *q = '\0';
  508.         if (*p == '\0')
  509.             quit (1,"No cryptkey found in %s\n",cryptkey);
  510.         cryptkey = salloc (buf);
  511.     }
  512.     (void) fclose (f);
  513.     x = request (dbgportsq ? DEBUGFPORT : FILEPORT,clienthost,&maxsleep);
  514.     if (x != SCMOK)
  515.         quit (1,"Unable to connect to host %s\n",clienthost);
  516.     x = msgsignon ();
  517.     if (x != SCMOK)
  518.         quit (1,"Error sending signon request to fileserver\n");
  519.     x = msgsignonack ();
  520.     if (x != SCMOK)
  521.         quit (1,"Error reading signon reply from fileserver\n");
  522.     printf ("SUP Fileserver %d.%d (%s) %d on %s\n",
  523.         protver,pgmver,scmver,fspid,remotehost());
  524.     free (scmver);
  525.     scmver = NULL;
  526.     if (protver < 7)
  527.         quit (1,"Remote fileserver does not implement reverse sup\n");
  528.     xpatch = TRUE;
  529.     xuser = clientuser;
  530.     x = msgsetup ();
  531.     if (x != SCMOK)
  532.         quit (1,"Error sending setup request to fileserver\n");
  533.     x = msgsetupack ();
  534.     if (x != SCMOK)
  535.         quit (1,"Error reading setup reply from fileserver\n");
  536.     switch (setupack) {
  537.     case FSETUPOK:
  538.         break;
  539.     case FSETUPSAME:
  540.         quit (1,"User %s not found on remote client\n",xuser);
  541.     case FSETUPHOST:
  542.         quit (1,"This host has no permission to reverse sup\n");
  543.     default:
  544.         quit (1,"Unrecognized file server setup status %d\n",setupack);
  545.     }
  546.     if (netcrypt (cryptkey) != SCMOK )
  547.         quit (1,"Running non-crypting fileserver\n");
  548.     crypttest = CRYPTTEST;
  549.     x = msgcrypt ();
  550.     if (x != SCMOK)
  551.         quit (1,"Error sending encryption test request\n");
  552.     x = msgcryptok ();
  553.     if (x == SCMEOF)
  554.         quit (1,"Data encryption test failed\n");
  555.     if (x != SCMOK)
  556.         quit (1,"Error reading encryption test reply\n");
  557.     logcrypt = CRYPTTEST;
  558.     loguser = NULL;
  559.     logpswd = NULL;
  560.     if (netcrypt (PSWDCRYPT) != SCMOK)    /* encrypt password data */
  561.         quit (1,"Running non-crypting fileserver\n");
  562.     x = msglogin ();
  563.     (void) netcrypt ((char *)NULL);    /* turn off encryption */
  564.     if (x != SCMOK)
  565.         quit (1,"Error sending login request to file server\n");
  566.     x = msglogack ();
  567.     if (x != SCMOK)
  568.         quit (1,"Error reading login reply from file server\n");
  569.     if (logack == FLOGNG)
  570.         quit (1,"%s\nImproper login to %s account\n",logerror,xuser);
  571.     xargc = argc;
  572.     xargv = argv;
  573.     x = msgxpatch ();
  574.     if (x != SCMOK)
  575.         quit (1,"Error sending crosspatch request\n");
  576.         crosspatch ();
  577.     exit (0);
  578. }
  579.  
  580. /*****************************************
  581.  ***    A N S W E R   R E Q U E S T    ***
  582.  *****************************************/
  583.  
  584. answer ()
  585. {
  586.     long starttime;
  587.     register int x;
  588.  
  589.     progpid = fspid = getpid ();
  590.     collname = NULL;
  591.     basedir = NULL;
  592.     prefix = NULL;
  593.     release = NULL;
  594.         rcs_branch = NULL;
  595. #ifdef CVS
  596.         cvs_root = NULL;
  597. #endif
  598.     goawayreason = NULL;
  599.     donereason = NULL;
  600.     lockfd = -1;
  601.     starttime = time ((long *)NULL);
  602.     if (!setjmp (sjbuf)) {
  603.         signon ();
  604.         setup ();
  605.         docrypt ();
  606.         login ();
  607.         if (xpatch) {
  608.             int fd;
  609.  
  610.             x = msgxpatch ();
  611.             if (x != SCMOK)
  612.                 exit (0);
  613.             xargv[0] = "sup";
  614.             xargv[1] = "-X";
  615.             xargv[xargc] = (char *)NULL;
  616.             (void) dup2 (netfile,0);
  617.             (void) dup2 (netfile,1);
  618.             (void) dup2 (netfile,2);
  619.             fd = getdtablesize ();
  620.             while (--fd > 2)
  621.                 (void) close (fd);
  622.             execvp (xargv[0],xargv);
  623.             exit (0);
  624.         }
  625.         listfiles ();
  626.         sendfiles ();
  627.     }
  628.     finishup (starttime);
  629.     if (collname)  free (collname);
  630.     if (basedir)  free (basedir);
  631.     if (prefix)  free (prefix);
  632.     if (release)  free (release);
  633.     if (rcs_branch)  free (rcs_branch);
  634. #ifdef CVS
  635.     if (cvs_root)  free (cvs_root);
  636. #endif
  637.     if (goawayreason) {
  638.         if (donereason == goawayreason)
  639.             donereason = NULL;
  640.         free (goawayreason);
  641.     }
  642.     if (donereason)  free (donereason);
  643.     if (lockfd >= 0)  (void) close (lockfd);
  644.     endpwent ();
  645.     (void) endgrent ();
  646. #if    CMUCS
  647.     endacent ();
  648. #endif    /* CMUCS */
  649.     Hfree (uidH);
  650.     Hfree (gidH);
  651.     Hfree (inodeH);
  652. }
  653.  
  654. /*****************************************
  655.  ***    S I G N   O N   C L I E N T    ***
  656.  *****************************************/
  657.  
  658. signon ()
  659. {
  660.     register int x;
  661.  
  662.     xpatch = FALSE;
  663.     x = msgsignon ();
  664.     if (x != SCMOK)  goaway ("Error reading signon request from client");
  665.     x = msgsignonack ();
  666.     if (x != SCMOK)  goaway ("Error sending signon reply to client");
  667.     free (scmver);
  668.     scmver = NULL;
  669. }
  670.  
  671. /*****************************************************************
  672.  ***    E X C H A N G E   S E T U P   I N F O R M A T I O N    ***
  673.  *****************************************************************/
  674.  
  675. setup ()
  676. {
  677.     register int x;
  678.     char *p,*q;
  679.     char buf[STRINGLENGTH];
  680.     register FILE *f;
  681.     struct stat sbuf;
  682.     register TREELIST *tl;
  683.  
  684.     if (protver > 7) {
  685.         cancompress = TRUE;
  686.     }
  687.     x = msgsetup ();
  688.     if (x != SCMOK)  goaway ("Error reading setup request from client");
  689.     if (protver < 4) {
  690.         setupack = FSETUPOLD;
  691.         (void) msgsetupack ();
  692.         if (protver >= 6)  longjmp (sjbuf,TRUE);
  693.         goaway ("Sup client using obsolete version of protocol");
  694.     }
  695.     if (xpatch) {
  696.         register struct passwd *pw;
  697.         extern int link_nofollow(), local_file();
  698.  
  699.         if ((pw = getpwnam (xuser)) == NULL) {
  700.             setupack = FSETUPSAME;
  701.             (void) msgsetupack ();
  702.             if (protver >= 6)  longjmp (sjbuf,TRUE);
  703.             goaway ("User not found");
  704.         }
  705.         (void) free (xuser);
  706.         xuser = salloc (pw->pw_dir);
  707.  
  708.         /* check crosspatch host access file */
  709.         cryptkey = NULL;
  710.         (void) sprintf (buf,FILEXPATCH,xuser);
  711.  
  712.         /* Turn off link following */
  713.         if (link_nofollow(1) != -1) {
  714.             int hostok = FALSE;
  715.             /* get stat info before open */
  716.             if (stat(buf, &sbuf) == -1)
  717.                 (void) bzero((char *)&sbuf, sizeof(sbuf));
  718.  
  719.             if ((f = fopen (buf,"r")) != NULL) {
  720.                 struct stat fsbuf;
  721.  
  722.                 while (p = fgets (buf,STRINGLENGTH,f)) {
  723.                     q = index (p,'\n');
  724.                     if (q)  *q = 0;
  725.                     if (index ("#;:",*p))  continue;
  726.                     q = nxtarg (&p," \t");
  727.                     if (*p == '\0')  continue;
  728.                     if (!matchhost(q)) continue;
  729.  
  730.                     cryptkey = salloc (p);
  731.                     hostok = TRUE;
  732.                     if (local_file(fileno(f), &fsbuf) > 0
  733.                         && stat_info_ok(&sbuf, &fsbuf)) {
  734.                         runas_uid = sbuf.st_uid;
  735.                         runas_gid = sbuf.st_gid;
  736.                     }
  737.                     break;
  738.                 }
  739.                 (void) fclose (f);
  740.             }
  741.  
  742.             /* Restore link following */
  743.             if (link_nofollow(0) == -1)
  744.                 goaway ("Restore link following");
  745.  
  746.             if (!hostok) {
  747.                 setupack = FSETUPHOST;
  748.                 (void) msgsetupack ();
  749.                 if (protver >= 6)  longjmp (sjbuf,TRUE);
  750.                 goaway ("Host not on access list");
  751.             }
  752.         }
  753.         setupack = FSETUPOK;
  754.         x = msgsetupack ();
  755.         if (x != SCMOK)
  756.             goaway ("Error sending setup reply to client");
  757.         return;
  758.     }
  759. #ifdef RCS
  760.         if (candorcs && release != NULL &&
  761.             (strncmp(release, "RCS.", 4) == 0)) {
  762.                 rcs_branch = salloc(&release[4]);
  763.                 free(release);
  764.                 release = salloc("RCS");
  765.                 dorcs = TRUE;
  766.         }
  767. #endif
  768.     if (release == NULL)
  769.         release = salloc (DEFRELEASE);
  770.     if (basedir == NULL || *basedir == '\0') {
  771.         basedir = NULL;
  772.         (void) sprintf (buf,FILEDIRS,DEFDIR);
  773.         f = fopen (buf,"r");
  774.         if (f) {
  775.             while (p = fgets (buf,STRINGLENGTH,f)) {
  776.                 q = index (p,'\n');
  777.                 if (q)  *q = 0;
  778.                 if (index ("#;:",*p))  continue;
  779.                 q = nxtarg (&p," \t=");
  780.                 if (strcmp(q,collname) == 0) {
  781.                     basedir = skipover(p," \t=");
  782.                     basedir = salloc (basedir);
  783.                     break;
  784.                 }
  785.             }
  786.             (void) fclose (f);
  787.         }
  788.         if (basedir == NULL) {
  789.             (void) sprintf (buf,FILEBASEDEFAULT,collname);
  790.             basedir = salloc(buf);
  791.         }
  792.     }
  793.     if (chdir (basedir) < 0)
  794.         goaway ("Can't chdir to base directory %s",basedir);
  795.     (void) sprintf (buf,FILEPREFIX,collname);
  796.     f = fopen (buf,"r");
  797.     if (f) {
  798.         while (p = fgets (buf,STRINGLENGTH,f)) {
  799.             q = index (p,'\n');
  800.             if (q)  *q = 0;
  801.             if (index ("#;:",*p))  continue;
  802.             prefix = salloc(p);
  803.             if (chdir (prefix) < 0)
  804.                 goaway ("Can't chdir to %s from base directory %s",
  805.                     prefix,basedir);
  806.             break;
  807.         }
  808.         (void) fclose (f);
  809.     }
  810.     x = stat (".",&sbuf);
  811.     if (prefix)  (void) chdir (basedir);
  812.     if (x < 0)
  813.         goaway ("Can't stat base/prefix directory");
  814.     if (nchildren >= maxchildren) {
  815.         setupack = FSETUPBUSY;
  816.         (void) msgsetupack ();
  817.         if (protver >= 6)  longjmp (sjbuf,TRUE);
  818.         goaway ("Sup client told to try again later");
  819.     }
  820.     if (sbuf.st_dev == basedev && sbuf.st_ino == baseino && samehost()) {
  821.         setupack = FSETUPSAME;
  822.         (void) msgsetupack ();
  823.         if (protver >= 6)  longjmp (sjbuf,TRUE);
  824.         goaway ("Attempt to upgrade to same directory on same host");
  825.     }
  826.     /* obtain release information */
  827.     if (!getrelease (release)) {
  828.         setupack = FSETUPRELEASE;
  829.         (void) msgsetupack ();
  830.         if (protver >= 6)  longjmp (sjbuf,TRUE);
  831.         goaway ("Invalid release information");
  832.     }
  833.     /* check host access file */
  834.     cryptkey = NULL;
  835.     for (tl = listTL; tl != NULL; tl = tl->TLnext) {
  836.         char *h;
  837.         if ((h = tl->TLhost) == NULL)
  838.             h = FILEHOSTDEF;
  839.         (void) sprintf (buf,FILEHOST,collname,h);
  840.         f = fopen (buf,"r");
  841.         if (f) {
  842.             int hostok = FALSE;
  843.             while (p = fgets (buf,STRINGLENGTH,f)) {
  844.                 int not;
  845.                 q = index (p,'\n');
  846.                 if (q)  *q = 0;
  847.                 if (index ("#;:",*p))  continue;
  848.                 q = nxtarg (&p," \t");
  849.                 if ((not = (*q == '!')) && *++q == '\0')
  850.                     q = nxtarg (&p," \t");
  851.                 hostok = (not == (matchhost(q) == 0));
  852.                 if (hostok) {
  853.                     while ((*p == ' ') || (*p == '\t')) p++;
  854.                     if (*p)  cryptkey = salloc (p);
  855.                     break;
  856.                 }
  857.             }
  858.             (void) fclose (f);
  859.             if (!hostok) {
  860.                 setupack = FSETUPHOST;
  861.                 (void) msgsetupack ();
  862.                 if (protver >= 6)  longjmp (sjbuf,TRUE);
  863.                 goaway ("Host not on access list for %s",
  864.                     collname);
  865.             }
  866.         }
  867.     }
  868.     /* try to lock collection */
  869.     (void) sprintf (buf,FILELOCK,collname);
  870.     x = open (buf,O_RDONLY,0);
  871.     if (x >= 0) {
  872.         if (flock (x,(LOCK_SH|LOCK_NB)) < 0) {
  873.             (void) close (x);
  874.             if (errno != EWOULDBLOCK)
  875.                 goaway ("Can't lock collection %s",collname);
  876.             setupack = FSETUPBUSY;
  877.             (void) msgsetupack ();
  878.             if (protver >= 6)  longjmp (sjbuf,TRUE);
  879.             goaway ("Sup client told to wait for lock");
  880.         }
  881.         lockfd = x;
  882.     }
  883.     setupack = FSETUPOK;
  884.     x = msgsetupack ();
  885.     if (x != SCMOK)  goaway ("Error sending setup reply to client");
  886. }
  887.  
  888. /** Test data encryption **/
  889. docrypt ()
  890. {
  891.     register int x;
  892.     char *p,*q;
  893.     char buf[STRINGLENGTH];
  894.     register FILE *f;
  895.     struct stat sbuf;
  896.     extern int  link_nofollow(), local_file();
  897.  
  898.     if (!xpatch) {
  899.         (void) sprintf (buf,FILECRYPT,collname);
  900.  
  901.         /* Turn off link following */
  902.         if (link_nofollow(1) != -1) {
  903.             /* get stat info before open */
  904.             if (stat(buf, &sbuf) == -1)
  905.                 (void) bzero((char *)&sbuf, sizeof(sbuf));
  906.  
  907.             if ((f = fopen (buf,"r")) != NULL) {
  908.                 struct stat fsbuf;
  909.  
  910.                 if (cryptkey == NULL &&
  911.                     (p = fgets (buf,STRINGLENGTH,f))) {
  912.                     if (q = index (p,'\n'))  *q = '\0';
  913.                     if (*p)  cryptkey = salloc (buf);
  914.                 }
  915.                 if (local_file(fileno(f), &fsbuf) > 0
  916.                     && stat_info_ok(&sbuf, &fsbuf)) {
  917.                     runas_uid = sbuf.st_uid;
  918.                     runas_gid = sbuf.st_gid;
  919.                 }
  920.                 (void) fclose (f);
  921.             }
  922.             /* Restore link following */
  923.             if (link_nofollow(0) == -1)
  924.                 goaway ("Restore link following");
  925.         }
  926.     }
  927.     if ( netcrypt (cryptkey) != SCMOK )
  928.         goaway ("Runing non-crypting supfilesrv");
  929.     x = msgcrypt ();
  930.     if (x != SCMOK)
  931.         goaway ("Error reading encryption test request from client");
  932.     (void) netcrypt ((char *)NULL);
  933.     if (strcmp(crypttest,CRYPTTEST) != 0)
  934.         goaway ("Client not encrypting data properly");
  935.     free (crypttest);
  936.     crypttest = NULL;
  937.     x = msgcryptok ();
  938.     if (x != SCMOK)
  939.         goaway ("Error sending encryption test reply to client");
  940. }
  941.  
  942. /***************************************************************
  943.  ***    C O N N E C T   T O   P R O P E R   A C C O U N T    ***
  944.  ***************************************************************/
  945.  
  946. login ()
  947. {
  948.     char *changeuid ();
  949.     register int x,fileuid,filegid;
  950.  
  951.     (void) netcrypt (PSWDCRYPT);    /* encrypt acct name and password */
  952.     x = msglogin ();
  953.     (void) netcrypt ((char *)NULL); /* turn off encryption */
  954.     if (x != SCMOK)  goaway ("Error reading login request from client");
  955.     if ( logcrypt ) {
  956.         if (strcmp(logcrypt,CRYPTTEST) != 0) {
  957.         logack = FLOGNG;
  958.         logerror = "Improper login encryption";
  959.         (void) msglogack ();
  960.         goaway ("Client not encrypting login information properly");
  961.         }
  962.         free (logcrypt);
  963.         logcrypt = NULL;
  964.     }
  965.     if (loguser == NULL) {
  966.         if (cryptkey) {
  967.             if (runas_uid >= 0 && runas_gid >= 0) {
  968.                 fileuid = runas_uid;
  969.                 filegid = runas_gid;
  970.                 loguser = NULL;
  971.             } else
  972.                 loguser = salloc (DEFUSER);
  973.         } else
  974.             loguser = salloc (DEFUSER);
  975.     }
  976.     if ((logerror = changeuid (loguser,logpswd,fileuid,filegid)) != NULL) {
  977.         logack = FLOGNG;
  978.         (void) msglogack ();
  979.         if (protver >= 6)  longjmp (sjbuf,TRUE);
  980.         goaway ("Client denied login access");
  981.     }
  982.     if (loguser)  free (loguser);
  983.     if (logpswd)  free (logpswd);
  984.     logack = FLOGOK;
  985.     x = msglogack ();
  986.     if (x != SCMOK)  goaway ("Error sending login reply to client");
  987.     if (!xpatch)  /* restore desired encryption */
  988.         if (netcrypt (cryptkey) != SCMOK)
  989.             goaway("Running non-crypting supfilesrv");
  990.     free (cryptkey);
  991.     cryptkey = NULL;
  992. }
  993.  
  994. /*****************************************
  995.  ***    M A K E   N A M E   L I S T    ***
  996.  *****************************************/
  997.  
  998. listfiles ()
  999. {
  1000.     int denyone();
  1001.     register int x;
  1002.  
  1003.     refuseT = NULL;
  1004.     x = msgrefuse ();
  1005.     if (x != SCMOK)  goaway ("Error reading refuse list from client");
  1006.     getscanlists ();
  1007.     Tfree (&refuseT);
  1008.     x = msglist ();
  1009.     if (x != SCMOK)  goaway ("Error sending file list to client");
  1010.     Tfree (&listT);
  1011.     listT = NULL;
  1012.     needT = NULL;
  1013.     x = msgneed ();
  1014.     if (x != SCMOK)
  1015.         goaway ("Error reading needed files list from client");
  1016.     denyT = NULL;
  1017.     (void) Tprocess (needT,denyone);
  1018.     Tfree (&needT);
  1019.     x = msgdeny ();
  1020.     if (x != SCMOK)  goaway ("Error sending denied files list to client");
  1021.     Tfree (&denyT);
  1022. }
  1023.  
  1024. denyone (t)
  1025. register TREE *t;
  1026. {
  1027.     register TREELIST *tl;
  1028.     register char *name = t->Tname;
  1029.     register int update = (t->Tflags&FUPDATE) != 0;
  1030.     struct stat sbuf;
  1031.     register TREE *tlink;
  1032.     TREE *linkcheck ();
  1033.     char slinkname[STRINGLENGTH];
  1034.     register int x;
  1035.  
  1036.     for (tl = listTL; tl != NULL; tl = tl->TLnext)
  1037.         if ((t = Tsearch (tl->TLtree,name)) != NULL)
  1038.             break;
  1039.     if (t == NULL) {
  1040.         (void) Tinsert (&denyT,name,FALSE);
  1041.         return (SCMOK);
  1042.     }
  1043.     cdprefix (tl->TLprefix);
  1044.     if ((t->Tmode&S_IFMT) == S_IFLNK)
  1045.         x = lstat(name,&sbuf);
  1046.     else
  1047.         x = stat(name,&sbuf);
  1048.     if (x < 0 || (sbuf.st_mode&S_IFMT) != (t->Tmode&S_IFMT)) {
  1049.         (void) Tinsert (&denyT,name,FALSE);
  1050.         return (SCMOK);
  1051.     }
  1052.     switch (t->Tmode&S_IFMT) {
  1053.     case S_IFLNK:
  1054.         if ((x = readlink (name,slinkname,STRINGLENGTH)) <= 0) {
  1055.             (void) Tinsert (&denyT,name,FALSE);
  1056.             return (SCMOK);
  1057.         }
  1058.         slinkname[x] = '\0';
  1059.         (void) Tinsert (&t->Tlink,slinkname,FALSE);
  1060.         break;
  1061.     case S_IFREG:
  1062.         if (sbuf.st_nlink > 1 &&
  1063.             (tlink = linkcheck (t,(int)sbuf.st_dev,(int)sbuf.st_ino)))
  1064.         {
  1065.             (void) Tinsert (&tlink->Tlink,name,FALSE);
  1066.             return (SCMOK);
  1067.         }
  1068.         if (update)  t->Tflags |= FUPDATE;
  1069.     case S_IFDIR:
  1070.         t->Tuid = sbuf.st_uid;
  1071.         t->Tgid = sbuf.st_gid;
  1072.         break;
  1073.     default:
  1074.         (void) Tinsert (&denyT,name,FALSE);
  1075.         return (SCMOK);
  1076.     }
  1077.     t->Tflags |= FNEEDED;
  1078.     return (SCMOK);
  1079. }
  1080.  
  1081. /*********************************
  1082.  ***    S E N D   F I L E S    ***
  1083.  *********************************/
  1084.  
  1085. sendfiles ()
  1086. {
  1087.     int sendone(),senddir(),sendfile();
  1088.     register TREELIST *tl;
  1089.     register int x;
  1090.  
  1091.     /* Does the protocol support compression */
  1092.     if (cancompress) {
  1093.         /* Check for compression on sending files */
  1094.         x = msgcompress();
  1095.         if ( x != SCMOK)
  1096.             goaway ("Error sending compression check to server");
  1097.     }
  1098.     /* send all files */
  1099.     for (tl = listTL; tl != NULL; tl = tl->TLnext) {
  1100.         cdprefix (tl->TLprefix);
  1101. #ifdef CVS
  1102.                 if (candorcs) {
  1103.                         cvs_root = getcwd(NULL, 256);
  1104.                         if (access("CVSROOT", F_OK) < 0)
  1105.                                 dorcs = FALSE;
  1106.                         else {
  1107.                                 loginfo("is a CVSROOT \"%s\"\n", cvs_root);
  1108.                                 dorcs = TRUE;
  1109.                         }
  1110.                 }
  1111. #endif
  1112.         (void) Tprocess (tl->TLtree,sendone);
  1113.     }
  1114.     /* send directories in reverse order */
  1115.     for (tl = listTL; tl != NULL; tl = tl->TLnext) {
  1116.         cdprefix (tl->TLprefix);
  1117.         (void) Trprocess (tl->TLtree,senddir);
  1118.     }
  1119.     x = msgsend ();
  1120.     if (x != SCMOK)
  1121.         goaway ("Error reading receive file request from client");
  1122.     upgradeT = NULL;
  1123.     x = msgrecv (sendfile,0);
  1124.     if (x != SCMOK)
  1125.         goaway ("Error sending file to client");
  1126. }
  1127.  
  1128. sendone (t)
  1129. TREE *t;
  1130. {
  1131.     register int x,fd;
  1132.     register int fdtmp;
  1133.     char sys_com[STRINGLENGTH], temp_file[STRINGLENGTH], rcs_file[STRINGLENGTH];
  1134.         union wait status;
  1135.     char *uconvert(),*gconvert();
  1136.     int sendfile ();
  1137.  
  1138.     if ((t->Tflags&FNEEDED) == 0)    /* only send needed files */
  1139.         return (SCMOK);
  1140.     if ((t->Tmode&S_IFMT) == S_IFDIR) /* send no directories this pass */
  1141.         return (SCMOK);
  1142.     x = msgsend ();
  1143.     if (x != SCMOK)  goaway ("Error reading receive file request from client");
  1144.     upgradeT = t;            /* upgrade file pointer */
  1145.     fd = -1;            /* no open file */
  1146.     if ((t->Tmode&S_IFMT) == S_IFREG) {
  1147.         if (!listonly && (t->Tflags&FUPDATE) == 0) {
  1148. #ifdef RCS
  1149.                         if (dorcs) {
  1150.                                 char rcs_release[STRINGLENGTH];
  1151.  
  1152.                 tmpnam(rcs_file);
  1153.                                 if (strcmp(&t->Tname[strlen(t->Tname)-2], ",v") == 0) {
  1154.                                         t->Tname[strlen(t->Tname)-2] = '\0';
  1155.                                         if (rcs_branch != NULL)
  1156. #ifdef CVS
  1157.                                                 sprintf(rcs_release, "-r %s", rcs_branch);
  1158. #else
  1159.                                                 sprintf(rcs_release, "-r%s", rcs_branch);
  1160. #endif
  1161.                                         else
  1162.                                                 rcs_release[0] = '\0';
  1163. #ifdef CVS
  1164.                                         sprintf(sys_com, "cvs -d %s -r -l -Q co -p %s %s > %s\n", cvs_root, rcs_release, t->Tname, rcs_file);
  1165. #else
  1166.                                         sprintf(sys_com, "co -q -p %s %s > %s 2> /dev/null\n", rcs_release, t->Tname, rcs_file);
  1167. #endif
  1168.                                         /*loginfo("using rcs mode \"%s\"\n", sys_com);*/
  1169.                                         status.w_status = system(sys_com);
  1170.                                         if (status.w_status < 0 || status.w_retcode) {
  1171.                                                 /* Just in case */
  1172.                                                 unlink(rcs_file);
  1173.                                                 if (status.w_status < 0) {
  1174.                                                         goaway ("We died trying to \"%s\"", sys_com);
  1175.                                                         t->Tmode = 0;
  1176.                                                 }
  1177.                                                 else {
  1178.                                                         /*logerr("rcs command failed \"%s\" = %d\n",
  1179.                                                                sys_com, status.w_retcode);*/
  1180.                                                         t->Tflags |= FUPDATE;
  1181.                                                 }
  1182.                                         }
  1183.                                         else if (docompress) {
  1184.                                                 tmpnam(temp_file);
  1185.                                                 sprintf(sys_com, "gzip -c < %s > %s\n", rcs_file, temp_file);
  1186.                                                 if (system(sys_com) < 0) {
  1187.                                                         /* Just in case */
  1188.                                                         unlink(temp_file);
  1189.                                                         unlink(rcs_file);
  1190.                                                         goaway ("We died trying to \"%s\"", sys_com);
  1191.                                                         t->Tmode = 0;
  1192.                                                 }
  1193.                                                 fd = open (temp_file,O_RDONLY,0);
  1194.                                         }
  1195.                                         else
  1196.                                                 fd = open (rcs_file,O_RDONLY,0);
  1197.                                 }
  1198.                         }
  1199. #endif
  1200.                         if (fd == -1) {
  1201.                                 if (docompress) {
  1202.                                         tmpnam(temp_file);
  1203.                                         sprintf(sys_com, "gzip -c < %s > %s\n", t->Tname, temp_file);
  1204.                                         if (system(sys_com) < 0) {
  1205.                                                 /* Just in case */
  1206.                                                 unlink(temp_file);
  1207.                                                 goaway ("We died trying to \"%s\"", sys_com);
  1208.                                                 t->Tmode = 0;
  1209.                                         }
  1210.                                         fd = open (temp_file,O_RDONLY,0);
  1211.                                 }
  1212.                                 else
  1213.                                         fd = open (t->Tname,O_RDONLY,0);
  1214.                         }
  1215.             if (fd < 0 && (t->Tflags&FUPDATE) == 0)  t->Tmode = 0;
  1216.         }
  1217.         if (t->Tmode) {
  1218.             t->Tuser = salloc (uconvert (t->Tuid));
  1219.             t->Tgroup = salloc (gconvert (t->Tgid));
  1220.         }
  1221.     }
  1222.     x = msgrecv (sendfile,fd);
  1223.     if (docompress)
  1224.         unlink(temp_file);
  1225. #ifdef RCS
  1226.     if (dorcs)
  1227.         unlink(rcs_file);
  1228. #endif
  1229.     if (x != SCMOK)  goaway ("Error sending file to client");
  1230.     return (SCMOK);
  1231. }
  1232.  
  1233. senddir (t)
  1234. TREE *t;
  1235. {
  1236.     register int x;
  1237.     char *uconvert(),*gconvert();
  1238.     int sendfile ();
  1239.  
  1240.     if ((t->Tflags&FNEEDED) == 0)    /* only send needed files */
  1241.         return (SCMOK);
  1242.     if ((t->Tmode&S_IFMT) != S_IFDIR) /* send only directories this pass */
  1243.         return (SCMOK);
  1244.     x = msgsend ();
  1245.     if (x != SCMOK)  goaway ("Error reading receive file request from client");
  1246.     upgradeT = t;            /* upgrade file pointer */
  1247.     t->Tuser = salloc (uconvert (t->Tuid));
  1248.     t->Tgroup = salloc (gconvert (t->Tgid));
  1249.     x = msgrecv (sendfile,0);
  1250.     if (x != SCMOK)  goaway ("Error sending file to client");
  1251.     return (SCMOK);
  1252. }
  1253.  
  1254. sendfile (t,ap)
  1255. register TREE *t;
  1256. va_list ap;
  1257. {
  1258.     register int x;
  1259.     int fd = va_arg(ap,int);
  1260.     if ((t->Tmode&S_IFMT) != S_IFREG || listonly || (t->Tflags&FUPDATE))
  1261.         return (SCMOK);
  1262.     x = writefile (fd);
  1263.     if (x != SCMOK)  goaway ("Error sending file to client");
  1264.         (void) close (fd);
  1265.     return (SCMOK);
  1266. }
  1267.  
  1268. /*****************************************
  1269.  ***    E N D   C O N N E C T I O N    ***
  1270.  *****************************************/
  1271.  
  1272. finishup (starttime)
  1273. long starttime;
  1274. {
  1275.     register int x = SCMOK;
  1276.     char tmpbuf[BUFSIZ], *p, lognam[STRINGLENGTH];
  1277.     int logfd;
  1278.     struct stat sbuf;
  1279.     long finishtime;
  1280.     char *releasename;
  1281.  
  1282.     (void) netcrypt ((char *)NULL);
  1283.     if (protver < 6) {
  1284.         if (goawayreason != NULL)
  1285.             free (goawayreason);
  1286.         goawayreason = (char *)NULL;
  1287.         x = msggoaway();
  1288.         doneack = FDONESUCCESS;
  1289.         donereason = salloc ("Unknown");
  1290.     } else if (goawayreason == (char *)NULL)
  1291.         x = msgdone ();
  1292.     else {
  1293.         doneack = FDONEGOAWAY;
  1294.         donereason = goawayreason;
  1295.     }
  1296.     if (x == SCMEOF || x == SCMERR) {
  1297.         doneack = FDONEUSRERROR;
  1298.         donereason = salloc ("Premature EOF on network");
  1299.     } else if (x != SCMOK) {
  1300.         doneack = FDONESRVERROR;
  1301.         donereason = salloc ("Unknown SCM code");
  1302.     }
  1303.     if (doneack == FDONEDONTLOG)
  1304.         return;
  1305.     if (donereason == NULL)
  1306.         donereason = salloc ("No reason");
  1307.     if (doneack == FDONESRVERROR || doneack == FDONEUSRERROR)
  1308.         logerr ("%s", donereason);
  1309.     else if (doneack == FDONEGOAWAY)
  1310.         logerr ("GOAWAY: %s",donereason);
  1311.     else if (doneack != FDONESUCCESS)
  1312.         logerr ("Reason %d:  %s",doneack,donereason);
  1313.     goawayreason = donereason;
  1314.     cdprefix ((char *)NULL);
  1315.     (void) sprintf (lognam,FILELOGFILE,collname);
  1316.     if ((logfd = open(lognam,O_APPEND|O_WRONLY,0644)) < 0)
  1317.         return; /* can not open file up...error */
  1318.     finishtime = time ((long *)NULL);
  1319.     p = tmpbuf;
  1320.     (void) sprintf (p,"%s ",fmttime (lasttime));
  1321.     p += strlen(p);
  1322.     (void) sprintf (p,"%s ",fmttime (starttime));
  1323.     p += strlen(p);
  1324.     (void) sprintf (p,"%s ",fmttime (finishtime));
  1325.     p += strlen(p);
  1326.     if ((releasename = release) == NULL)
  1327.         releasename = "UNKNOWN";
  1328.     (void) sprintf (p,"%s %s %d %s\n",remotehost(),releasename,
  1329.         FDONESUCCESS-doneack,donereason);
  1330.     p += strlen(p);
  1331. #if    MACH
  1332.     /* if we are busy dont get stuck updating the disk if full */
  1333.     if(setupack == FSETUPBUSY) {
  1334.         long l = FIOCNOSPC_ERROR;
  1335.         ioctl(logfd, FIOCNOSPC, &l);
  1336.     }
  1337. #endif    /* MACH */
  1338.     (void) write(logfd,tmpbuf,(p - tmpbuf));
  1339.     (void) close(logfd);
  1340. }
  1341.  
  1342. /***************************************************
  1343.  ***    H A S H   T A B L E   R O U T I N E S    ***
  1344.  ***************************************************/
  1345.  
  1346. Hfree (table)
  1347. HASH **table;
  1348. {
  1349.     register HASH *h;
  1350.     register int i;
  1351.     for (i = 0; i < HASHSIZE; i++)
  1352.         while (h = table[i]) {
  1353.             table[i] = h->Hnext;
  1354.             if (h->Hname)  free (h->Hname);
  1355.             free ((char *)h);
  1356.         }
  1357. }
  1358.  
  1359. HASH *Hlookup (table,num1,num2)
  1360. HASH **table;
  1361. int num1,num2;
  1362. {
  1363.     register HASH *h;
  1364.     register int hno;
  1365.     hno = HASHFUNC(num1,num2);
  1366.     for (h = table[hno]; h && (h->Hnum1 != num1 || h->Hnum2 != num2); h = h->Hnext);
  1367.     return (h);
  1368. }
  1369.  
  1370. Hinsert (table,num1,num2,name,tree)
  1371. HASH **table;
  1372. int num1,num2;
  1373. char *name;
  1374. TREE *tree;
  1375. {
  1376.     register HASH *h;
  1377.     register int hno;
  1378.     hno = HASHFUNC(num1,num2);
  1379.     h = (HASH *) malloc (sizeof(HASH));
  1380.     h->Hnum1 = num1;
  1381.     h->Hnum2 = num2;
  1382.     h->Hname = name;
  1383.     h->Htree = tree;
  1384.     h->Hnext = table[hno];
  1385.     table[hno] = h;
  1386. }
  1387.  
  1388. /*********************************************
  1389.  ***    U T I L I T Y   R O U T I N E S    ***
  1390.  *********************************************/
  1391.  
  1392. TREE *linkcheck (t,d,i)
  1393. TREE *t;
  1394. int d,i;            /* inode # and device # */
  1395. {
  1396.     register HASH *h;
  1397.     h = Hlookup (inodeH,i,d);
  1398.     if (h)  return (h->Htree);
  1399.     Hinsert (inodeH,i,d,(char *)NULL,t);
  1400.     return ((TREE *)NULL);
  1401. }
  1402.  
  1403. char *uconvert (uid)
  1404. int uid;
  1405. {
  1406.     register struct passwd *pw;
  1407.     register char *p;
  1408.     register HASH *u;
  1409.     u = Hlookup (uidH,uid,0);
  1410.     if (u)  return (u->Hname);
  1411.     pw = getpwuid (uid);
  1412.     if (pw == NULL)  return ("");
  1413.     p = salloc (pw->pw_name);
  1414.     Hinsert (uidH,uid,0,p,(TREE*)NULL);
  1415.     return (p);
  1416. }
  1417.  
  1418. char *gconvert (gid)
  1419. int gid;
  1420. {
  1421.     register struct group *gr;
  1422.     register char *p;
  1423.     register HASH *g;
  1424.     g = Hlookup (gidH,gid,0);
  1425.     if (g)  return (g->Hname);
  1426.     gr = getgrgid (gid);
  1427.     if (gr == NULL)  return ("");
  1428.     p = salloc (gr->gr_name);
  1429.     Hinsert (gidH,gid,0,p,(TREE *)NULL);
  1430.     return (p);
  1431. }
  1432.  
  1433. char *changeuid (namep,passwordp,fileuid,filegid)
  1434. char *namep,*passwordp;
  1435. int fileuid,filegid;
  1436. {
  1437.     char *okpassword ();
  1438.     char *group,*account,*pswdp;
  1439.     struct passwd *pwd;
  1440.     struct group *grp;
  1441. #if    CMUCS
  1442.     struct account *acc;
  1443.     struct ttyloc tlc;
  1444. #endif    /* CMUCS */
  1445.     register int status = ACCESS_CODE_OK;
  1446.     char nbuf[STRINGLENGTH];
  1447.     static char errbuf[STRINGLENGTH];
  1448. #if    CMUCS
  1449.     int *grps;
  1450. #endif    /* CMUCS */
  1451.     char *p;
  1452.  
  1453.     if (namep == NULL) {
  1454.         pwd = getpwuid (fileuid);
  1455.         if (pwd == NULL) {
  1456.             (void) sprintf (errbuf,"Reason:  Unknown user id %d",
  1457.                 fileuid);
  1458.             return (errbuf);
  1459.         }
  1460.         grp = getgrgid (filegid);
  1461.         if (grp)  group = strcpy (nbuf,grp->gr_name);
  1462.         else  group = NULL;
  1463.         account = NULL;
  1464.         pswdp = NULL;
  1465.     } else {
  1466.         (void) strcpy (nbuf,namep);
  1467.         account = group = index (nbuf,',');
  1468.         if (group != NULL) {
  1469.             *group++ = '\0';
  1470.             account = index (group,',');
  1471.             if (account != NULL) {
  1472.                 *account++ = '\0';
  1473.                 if (*account == '\0')  account = NULL;
  1474.             }
  1475.             if (*group == '\0')  group = NULL;
  1476.         }
  1477.         pwd = getpwnam (nbuf);
  1478.         if (pwd == NULL) {
  1479.             (void) sprintf (errbuf,"Reason:  Unknown user %s",
  1480.                 nbuf);
  1481.             return (errbuf);
  1482.         }
  1483.         if (strcmp (nbuf,DEFUSER) == 0)
  1484.             pswdp = NULL;
  1485.         else
  1486.             pswdp = passwordp ? passwordp : "";
  1487. #ifdef AFS
  1488.                 if (strcmp (nbuf,DEFUSER) != 0) {
  1489.                         char *reason;
  1490.                         setpag(); /* set a pag */
  1491.                         if (ka_UserAuthenticate(pwd->pw_name, "", 0,
  1492.                                                 pswdp, 1, &reason)) {
  1493.                                 (void) sprintf (errbuf,"AFS authentication failed, %s",
  1494.                                                 reason);
  1495.                                 logerr ("Attempt by %s; %s",
  1496.                                         nbuf, errbuf);
  1497.                                 return (errbuf);
  1498.                         }
  1499.                 }
  1500. #endif
  1501.     }
  1502.     if (getuid () != 0) {
  1503.         if (getuid () == pwd->pw_uid)
  1504.             return (NULL);
  1505.         if (strcmp (pwd->pw_name,DEFUSER) == 0)
  1506.             return (NULL);
  1507.         logerr ("Fileserver not superuser");
  1508.         return ("Reason:  fileserver is not running privileged");
  1509.     }
  1510. #if    CMUCS
  1511.     tlc.tlc_hostid = TLC_UNKHOST;
  1512.     tlc.tlc_ttyid = TLC_UNKTTY;
  1513.     if (okaccess(pwd->pw_name,ACCESS_TYPE_SU,0,-1,tlc) != 1)
  1514.         status = ACCESS_CODE_DENIED;
  1515.     else {
  1516.         grp = NULL;
  1517.         acc = NULL;
  1518.         status = oklogin(pwd->pw_name,group,&account,pswdp,&pwd,&grp,&acc,&grps);
  1519.         if (status == ACCESS_CODE_OK) {
  1520.             if ((p = okpassword(pswdp,pwd->pw_name,pwd->pw_gecos)) != NULL)
  1521.                 status = ACCESS_CODE_INSECUREPWD;
  1522.         }
  1523.     }
  1524. #else    /* CMUCS */
  1525.     status = ACCESS_CODE_OK;
  1526.     if (namep && strcmp(pwd->pw_name, DEFUSER) != 0)
  1527.         if (strcmp(pwd->pw_passwd,(char *)crypt(pswdp,pwd->pw_passwd)))
  1528.             status = ACCESS_CODE_BADPASSWORD;
  1529. #endif    /* CMUCS */
  1530.     switch (status) {
  1531.     case ACCESS_CODE_OK:
  1532.         break;
  1533.     case ACCESS_CODE_BADPASSWORD:
  1534.         p = "Reason:  Invalid password";
  1535.         break;
  1536. #if    CMUCS
  1537.     case ACCESS_CODE_INSECUREPWD:
  1538.         (void) sprintf (errbuf,"Reason:  %s",p);
  1539.         p = errbuf;
  1540.         break;
  1541.     case ACCESS_CODE_DENIED:
  1542.         p = "Reason:  Access denied";
  1543.         break;
  1544.     case ACCESS_CODE_NOUSER:
  1545.         p = errbuf;
  1546.         break;
  1547.     case ACCESS_CODE_ACCEXPIRED:
  1548.         p = "Reason:  Account expired";
  1549.         break;
  1550.     case ACCESS_CODE_GRPEXPIRED:
  1551.         p = "Reason:  Group expired";
  1552.         break;
  1553.     case ACCESS_CODE_ACCNOTVALID:
  1554.         p = "Reason:  Invalid account";
  1555.         break;
  1556.     case ACCESS_CODE_MANYDEFACC:
  1557.         p = "Reason:  User has more than one default account";
  1558.         break;
  1559.     case ACCESS_CODE_NOACCFORGRP:
  1560.         p = "Reason:  No account for group";
  1561.         break;
  1562.     case ACCESS_CODE_NOGRPFORACC:
  1563.         p = "Reason:  No group for account";
  1564.         break;
  1565.     case ACCESS_CODE_NOGRPDEFACC:
  1566.         p = "Reason:  No group for default account";
  1567.         break;
  1568.     case ACCESS_CODE_NOTGRPMEMB:
  1569.         p = "Reason:  Not member of group";
  1570.         break;
  1571.     case ACCESS_CODE_NOTDEFMEMB:
  1572.         p = "Reason:  Not member of default group";
  1573.         break;
  1574.     case ACCESS_CODE_OOPS:
  1575.         p = "Reason:  Internal error";
  1576.         break;
  1577. #endif    /* CMUCS */
  1578.     default:
  1579.         (void) sprintf (p = errbuf,"Reason:  Status %d",status);
  1580.         break;
  1581.     }
  1582.     if (pwd == NULL)
  1583.         return (p);
  1584.     if (status != ACCESS_CODE_OK) {
  1585.         logerr ("Login failure for %s",pwd->pw_name);
  1586.         logerr ("%s",p);
  1587. #if    CMUCS
  1588.         logaccess (pwd->pw_name,ACCESS_TYPE_SUP,status,0,-1,tlc);
  1589. #endif    /* CMUCS */
  1590.         return (p);
  1591.     }
  1592. #if    CMUCS
  1593.     if (setgroups (grps[0], &grps[1]) < 0)
  1594.         logerr ("setgroups: %%m");
  1595.     if (setgid ((gid_t)grp->gr_gid) < 0)
  1596.         logerr ("setgid: %%m");
  1597.     if (setuid ((uid_t)pwd->pw_uid) < 0)
  1598.         logerr ("setuid: %%m");
  1599. #else   /* CMUCS */
  1600.     if (initgroups (pwd->pw_name,pwd->pw_gid) < 0)
  1601.         return("Error setting group list");
  1602.     if (setgid (pwd->pw_gid) < 0)
  1603.         logerr ("setgid: %%m");
  1604.     if (setuid (pwd->pw_uid) < 0)
  1605.         logerr ("setuid: %%m");
  1606. #endif    /* CMUCS */
  1607.     return (NULL);
  1608. }
  1609.  
  1610. #if __STDC__
  1611. goaway (char *fmt,...)
  1612. #else
  1613. /*VARARGS*//*ARGSUSED*/
  1614. goaway (va_alist)
  1615. va_dcl
  1616. #endif
  1617. {
  1618. #if !__STDC__
  1619.     register char *fmt;
  1620. #endif
  1621.     char buf[STRINGLENGTH];
  1622.     va_list ap;
  1623.  
  1624.     (void) netcrypt ((char *)NULL);
  1625. #if __STDC__
  1626.     va_start(ap,fmt);
  1627. #else
  1628.     va_start(ap);
  1629.     fmt = va_arg(ap,char *);
  1630. #endif
  1631.     vsnprintf(buf, sizeof(buf), fmt, ap);
  1632.     va_end(ap);
  1633.     goawayreason = salloc (buf);
  1634.     (void) msggoaway ();
  1635.     logerr ("%s",buf);
  1636.     longjmp (sjbuf,TRUE);
  1637. }
  1638.  
  1639. char *fmttime (time)
  1640. long time;
  1641. {
  1642.     static char buf[STRINGLENGTH];
  1643.     int len;
  1644.  
  1645.     (void) strcpy (buf,ctime (&time));
  1646.     len = strlen(buf+4)-6;
  1647.     (void) strncpy (buf,buf+4,len);
  1648.     buf[len] = '\0';
  1649.     return (buf);
  1650. }
  1651.  
  1652. /*
  1653.  * Determine whether the file referenced by the file descriptor 'handle' can
  1654.  * be trusted, namely is it a file resident in the local file system.
  1655.  *
  1656.  * The main method of operation is to perform operations on the file
  1657.  * descriptor so that an attempt to spoof the checks should fail, for
  1658.  * example renamimg the file from underneath us and/or changing where the
  1659.  * file lives from underneath us.
  1660.  *
  1661.  * returns: -1 for error, indicating that we can not tell
  1662.  *         0 for file is definately not local, or it is an RFS link
  1663.  *         1 for file is local and can be trusted
  1664.  *
  1665.  * Side effect: copies the stat information into the supplied buffer,
  1666.  * regardless of the type of file system the file resides.
  1667.  *
  1668.  * Currently, the cases that we try to distinguish are RFS, AFS, NFS and
  1669.  * UFS, where the latter is considered a trusted file.  We assume that the
  1670.  * caller has disabled link following and will detect an attempt to access
  1671.  * a file through an RFS link, except in the case the the last component is
  1672.  * an RFS link.  With link following disabled, the last component itself is
  1673.  * interpreted as a regular file if it is really an RFS link, so we
  1674.  * disallow the RFS link identified by group "symlink" and mode "IEXEC by
  1675.  * owner only". An AFS file is
  1676.  * detected by trying the VIOCIGETCELL ioctl, which is one of the few AFS
  1677.  * ioctls which operate on a file descriptor.  Note, this AFS ioctl is
  1678.  * implemented in the cache manager, so the decision does not involve a
  1679.  * query with the AFS file server.  An NFS file is detected by looking at
  1680.  * the major device number and seeing if it matches the known values for
  1681.  * MACH NSF/Sun OS 3.x or Sun OS 4.x.
  1682.  *
  1683.  * Having the fstatfs() system call would make this routine easier and
  1684.  * more reliable.
  1685.  *
  1686.  * Note, in order to make the checks simpler, the file referenced by the
  1687.  * file descriptor can not be a BSD style symlink.  Even with symlink
  1688.  * following of the last path component disabled, the attempt to open a
  1689.  * file which is a symlink will succeed, so we check for the BSD symlink
  1690.  * file type here.  Also, the link following on/off and RFS file types
  1691.  * are only relevant in a MACH environment. 
  1692.  */
  1693. #ifdef    AFS
  1694. #include <sys/viceioctl.h>
  1695. #endif
  1696.  
  1697. #define SYMLINK_GRP 64
  1698.  
  1699. int local_file(handle, sinfo)
  1700. int handle;
  1701. struct stat *sinfo;
  1702. {
  1703.     struct stat sb;
  1704. #ifdef    VIOCIGETCELL
  1705.     /*
  1706.      * dummies for the AFS ioctl
  1707.      */
  1708.     struct ViceIoctl vdata;
  1709.     char cellname[512];
  1710. #endif    /* VIOCIGETCELL */
  1711.  
  1712.     if (fstat(handle, &sb) < 0)
  1713.         return(-1);
  1714.     if (sinfo != NULL)
  1715.         *sinfo = sb;
  1716.  
  1717. #if    CMUCS
  1718.     /*
  1719.      * If the following test succeeds, then the file referenced by
  1720.      * 'handle' is actually an RFS link, so we will not trust it.
  1721.      * See <sys/inode.h>.
  1722.      */
  1723.     if (sb.st_gid == SYMLINK_GRP
  1724.         && (sb.st_mode & (S_IFMT|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)))
  1725.             == (S_IFREG|S_IEXEC))
  1726.         return(0);
  1727. #endif    /* CMUCS */
  1728.  
  1729.     /*
  1730.      * Do not trust BSD style symlinks either.
  1731.      */
  1732.     if ((sb.st_mode & S_IFMT) == S_IFLNK)
  1733.         return(0);
  1734.  
  1735. #ifdef    VIOCIGETCELL
  1736.     /*
  1737.      * This is the VIOCIGETCELL ioctl, which takes an fd, not
  1738.      * a path name.  If it succeeds, then the file is in AFS.
  1739.      *
  1740.      * On failure, ENOTTY indicates that the file was not in
  1741.      * AFS; all other errors are pessimistically assumed to be
  1742.      * a temporary AFS error.
  1743.      */
  1744.     vdata.in_size = 0;
  1745.     vdata.out_size = sizeof(cellname);
  1746.     vdata.out = cellname;
  1747.     if (ioctl(handle, VIOCIGETCELL, (char *)&vdata) != -1)
  1748.         return(0);
  1749.     if (errno != ENOTTY)
  1750.         return(-1);
  1751. #endif    /* VIOCIGETCELL */
  1752.  
  1753.     /*
  1754.      * Verify the file is not in NFS.
  1755.      *
  1756.      * Our current implementation and Sun OS 3.x use major device
  1757.      * 255 for NFS files; Sun OS 4.x seems to use 130 (I have only
  1758.      * determined this empirically -- DLC).  Without a fstatfs()
  1759.      * system call, this will have to do for now.
  1760.      */
  1761.     if (major(sb.st_dev) == 255 || major(sb.st_dev) == 130)
  1762.         return(0);
  1763.  
  1764.     return(1);
  1765. }
  1766.  
  1767. /*
  1768.  * Companion routine for ensuring that a local file can be trusted.  Compare
  1769.  * various pieces of the stat information to make sure that the file can be
  1770.  * trusted.  Returns true for stat information which meets the criteria
  1771.  * for being trustworthy.  The main paranoia is to prevent a hard link to
  1772.  * a root owned file.  Since the link could be removed after the file is
  1773.  * opened, a simply fstat() can not be relied upon.  The two stat buffers
  1774.  * for comparison should come from a stat() on the file name and a following
  1775.  * fstat() on the open file.  Some of the following checks are also an
  1776.  * additional level of paranoia.  Also, this test will fail (correctly) if
  1777.  * either or both of the stat structures have all fields zeroed; typically
  1778.  * due to a stat() failure.
  1779.  */
  1780.  
  1781.  
  1782. int stat_info_ok(sb1, sb2)
  1783. struct stat *sb1, *sb2;
  1784. {
  1785.     return (sb1->st_ino == sb2->st_ino &&    /* Still the same file */
  1786.         sb1->st_dev == sb2->st_dev &&    /* On the same device */
  1787.         sb1->st_mode == sb2->st_mode &&     /* Perms (and type) same */
  1788.         (sb1->st_mode & S_IFMT) == S_IFREG && /* Only allow reg files */
  1789.         (sb1->st_mode & 077) == 0 &&    /* Owner only perms */
  1790.         sb1->st_nlink == sb2->st_nlink &&    /* # hard links same... */
  1791.         sb1->st_nlink == 1 &&        /* and only 1 */
  1792.         sb1->st_uid == sb2->st_uid &&    /* owner and ... */
  1793.         sb1->st_gid == sb2->st_gid &&    /* group unchanged */
  1794.         sb1->st_mtime == sb2->st_mtime &&    /* Unmodified between stats */
  1795.         sb1->st_ctime == sb2->st_ctime);    /* Inode unchanged.  Hopefully
  1796.                            a catch-all paranoid test */
  1797. }
  1798.  
  1799. #if MACH
  1800. /*
  1801.  * Twiddle symbolic/RFS link following on/off.  This is a no-op in a non
  1802.  * CMUCS/MACH environment.  Also, the setmodes/getmodes interface is used
  1803.  * mainly because it is simpler than using table(2) directly.
  1804.  */
  1805. #include <sys/table.h>
  1806.  
  1807. int link_nofollow(on)
  1808. int on;
  1809. {
  1810.     static int modes = -1;
  1811.  
  1812.     if (modes == -1 && (modes = getmodes()) == -1)
  1813.         return(-1);
  1814.     if (on)
  1815.         return(setmodes(modes | UMODE_NOFOLLOW));
  1816.     return(setmodes(modes));
  1817. }
  1818. #else    /* MACH */
  1819. /*ARGSUSED*/
  1820. int link_nofollow(on)
  1821. int on;
  1822. {
  1823.     return(0);
  1824. }
  1825. #endif    /* MACH */
  1826.